#pragma once
#include <vector>
#include <mutex>
#include <fstream>
#include <algorithm>
#include <jsoncpp/json/json.h>
#include "../comm/util.h"
#include "../comm/Log.h"
#include "../comm/httplib.h"
#include "oj_view.h"
#include "oj_model.h"

// 实现控制模块(业务逻辑整合)
namespace ns_control
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_model;
    using namespace ns_view;
    using namespace httplib;

    // 后端提供服务主机信息(描述)
    struct machine
    {
        machine(string ip = "0.0.0.0", uint16_t port = 0, uint32_t count = 0, mutex *mtx = nullptr)
            : _ip(ip), _port(port), _count(count), _mtx(mtx)
        {
        }
        void IncLoad()
        {
            if (_mtx)
            {
                _mtx->lock();
                _count++;
                _mtx->unlock();
            }
            else
                LOG(WARNING) << "未初始化锁资源" << endl;
        }
        void DecLoad()
        {
            if (_mtx)
            {
                _mtx->lock();
                _count--;
                _mtx->unlock();
            }
            else
                LOG(WARNING) << "未初始化锁资源" << endl;
        }
        string _ip;
        uint16_t _port;
        uint32_t _count; // 主机所连接的客户数量(负载)
        mutex *_mtx;     // c++的mutex不支持拷贝(保护负载count情况)
    };

    // 负载均衡业务逻辑(组织)
    const string machine_path = "./conf/server_machine_conf";
    const string sep = ":";
    class LoadBlance
    {
        // 将配置文件中保存的主机加载进去
        bool LoadConf()
        {
            ifstream in(machine_path);
            if (!in.is_open())
            {
                LOG(WARNING) << "文件打开失败" << endl;
            }

            // 读取ip+port
            string line;
            while (getline(in, line))
            {
                vector<string> out;
                str_util::SplitStr(line, &out, sep);
                if (out.size() != 2)
                {
                    LOG(WARNING) << "题库格式存在问题" << endl;
                    continue;
                }
                machine m(out[0], stoi(out[1]), 0, new mutex());
                _machines.push_back(m);
                _status.push_back(true);
            }
            in.close();
            return true;
        }

    public:
        LoadBlance()
        {
            assert(LoadConf());
        }

        // 负载均衡选择主机提供服务
        bool SmartChoose(int *inode, machine **m)
        {
            _mtx.lock();

            // 方法：遍历上线的所有主机,选出负载最小的一个
            size_t ret = UINT_MAX;
            for (int i = 0; i < _status.size(); i++)
            {
                int count = _machines[i]._count;
                if (_status[i] && ret > count)
                {
                    ret = count;
                    *inode = i;
                    *m = &_machines[i];
                }
            }
            _mtx.unlock();
            if (ret == UINT_MAX)
            {
                LOG(FATAL) << "后端无服务器上线" << endl;
                return false;
            }
            
            return true;
        }

        // 上线所有主机
        void OnLineAll()
        {
            _mtx.lock();
            for (int i = 0; i < _status.size(); i++)
            {
                _status[i] = true;
                _machines[i]._count = 0;
            }
            _mtx.unlock();
            LOG(DEBUG) << "所有主机再次上线" << endl;
        }

        // 离线主机
        void OffLine(int inode)
        {
            _mtx.lock();
            _status[inode] = false;
            _machines[inode]._count = 0;
            _mtx.unlock();
        }

    private:
        vector<machine> _machines; // 后端(conf)中的所有主机
        vector<bool> _status;      // 后端主机的上下线情况
        // 下标对应主机编号 true表示上线 false表示下线
        mutex _mtx; // 保证安全性
    };

    class Control
    {
    public:
        Control() {}
        ~Control() {}
        void OnlineAllMachine()
        {
            _loadblance.OnLineAll();
        }

    public:
        bool AllQuestions(string *html)
        {
            // 1.获取所有题目信息
            vector<Question> all;
            if (_model.GetAllQuestions(&all))
            {
                // 排序
                sort(all.begin(), all.end(), [](const Question &q1, const Question &q2) -> bool
                     { return stoi(q1._nub) < stoi(q2._nub); });
                LOG(INFO) << "获取所有题目并排序成功..." << endl;
            }
            else
            {
                LOG(ERROR) << "获取所有题目失败..." << endl;
                return false;
            }

            // 2.构建html
            if (_view.AllStructure(all, html))
            {
                LOG(INFO) << "题目列表加载成功..." << endl;
            }
            else
            {
                LOG(ERROR) << "题目列表加载失败..." << endl;
                return false;
            }
            return true;
        }

        bool OneQuestion(const string &nub, string *html)
        {
            // 1.获取制定题目信息
            Question one;
            if (_model.GetOneQuestion(nub, &one))
            {
                LOG(INFO) << "获取指定题目成功..." << endl;
            }
            else
            {
                LOG(ERROR) << "获取指定题目失败..." << endl;
                return false;
            }

            // 2.构建html
            if (_view.OneStructure(one, html))
            {
                LOG(INFO) << "题目加载成功..." << endl;
            }
            else
            {
                LOG(ERROR) << "题目加载失败..." << endl;
                return false;
            }
            return true;
        }

        // 序列化内容：{code, 输入的自测用例, cpu_limit, mem_limit}
        // json_in：客户端上传的 代码+测试用例
        bool Judge(const string nub, const string &json_in, string *json_out)
        {
            // 0.根据题号进行准备工作-》获取test测试用例(包含mian函数)
            Question out;
            _model.GetOneQuestion(nub, &out);
            int cpu_limit = out._cpu_limit;
            int mem_limit = out._mem_limit;
            string main_code = out._test;

            // 2.进行json序列化
            Json::Value in;
            Json::Reader reader;
            reader.parse(json_in, in);
            in["code"] = in["code"].asString() + main_code;
            in["cpu_limit"] = cpu_limit;
            in["mem_limit"] = mem_limit;
            Json::StyledWriter write;
            string compile_json = write.write(in);

            int inode;
            machine *m = nullptr;
            while (true)
            {
                // 3.负载均衡式选择后端主机提供编译服务
                if (!_loadblance.SmartChoose(&inode, &m))
                {
                    LOG(FATAL) << "后端所有主机已经离线" << endl;
                    // 需要运营上线所有后端主机
                    return false;
                }

                // 4.充当客户端进行请求->compile_run.cpp服务
                m->IncLoad(); // 主机负载+1
                LOG(INFO) << "主机负载+1 " << "ip = " << m->_ip << " port = " << m->_port << endl;

                Client cli(m->_ip, m->_port);
                Result res = cli.Post("/CompileAndRun", compile_json,
                                      "application/json;charset=utf-8");
                if (res)
                {
                    if (res->status != 200)
                        continue;

                    // 5.将compile_run.cpp的返回值拿来给客户端返回
                    *json_out = res->body;
                    break;
                }
                else
                {
                    // 重新选择后端上线主机
                    LOG(ERROR) << "发送请求失败,后端主机可能离线，重新加载..." << endl;
                    // 将该主机移出
                    _loadblance.OffLine(inode);
                    continue;
                }
            }
            m->DecLoad(); // 主机负载-1

            return true;
        }

    private:
        Model _model;
        View _view;
        LoadBlance _loadblance;
    };
};